package com.sojoys.artifact.third.rpc.client;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import com.sojoys.artifact.third.rpc.common.bean.RpcRequest;
import com.sojoys.artifact.third.rpc.common.bean.RpcResponse;
import com.sojoys.artifact.third.rpc.registry.ServiceDiscovery;
import com.xiaoleilu.hutool.log.Log;
import com.xiaoleilu.hutool.log.LogFactory;
import com.xiaoleilu.hutool.util.StrUtil;

/**
 * RPC 代理（用于创建 RPC 服务代理）
 *
 * @author huangyong
 * @since 1.0.0
 */
public class RpcProxy {

	private static final RpcProxy DEFAULT = new RpcProxy();

	public static RpcProxy me() {
		return DEFAULT;
	}
	
	static Log log = LogFactory.get(RpcProxy.class);

    private String serviceAddress;

    private ServiceDiscovery serviceDiscovery;
    
    /** 代理缓存 */
    private Table<Class<?>,String,Object> proxyCache = HashBasedTable.create();

    public RpcProxy(String serviceAddress) {
        this.serviceAddress = serviceAddress;
    }

    public RpcProxy(ServiceDiscovery serviceDiscovery) {
        this.serviceDiscovery = serviceDiscovery;
    }

    private RpcProxy() {
    	
	}

	@SuppressWarnings("unchecked")
    public <T> T create(final Class<?> interfaceClass) {
        return create(interfaceClass, "");
    }

    @SuppressWarnings("unchecked")
    public <T> T create(final Class<?> interfaceClass, final String serviceVersion) {
    	Object proxy = proxyCache.get(interfaceClass, serviceVersion);
    	if (proxy==null) {
    		// 创建动态代理对象
    		proxy = Proxy.newProxyInstance(
                    interfaceClass.getClassLoader(),
                    new Class<?>[]{interfaceClass},
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            // 创建 RPC 请求对象并设置请求属性
                            RpcRequest request = new RpcRequest();
                            request.setRequestId(UUID.randomUUID().toString());
                            request.setInterfaceName(method.getDeclaringClass().getName());
                            request.setServiceVersion(serviceVersion);
                            request.setMethodName(method.getName());
                            request.setParameterTypes(method.getParameterTypes());
                            request.setParameters(args);
                            // 获取 RPC 服务地址
                            if (serviceDiscovery != null) {
                                String serviceName = interfaceClass.getName();
                                if (StrUtil.isNotEmpty(serviceVersion)) {
                                    serviceName += "-" + serviceVersion;
                                }
                                serviceAddress = serviceDiscovery.discover(serviceName);
                                log.debug("discover service: {} => {}", serviceName, serviceAddress);
                            }
                            if (StrUtil.isEmpty(serviceAddress)) {
                                throw new RuntimeException("server address is empty");
                            }
                            // 从 RPC 服务地址中解析主机名与端口号
                            String[] array = StrUtil.split(serviceAddress, ":");
                            String host = array[0];
                            int port = Integer.parseInt(array[1]);
                            // 创建 RPC 客户端对象并发送 RPC 请求
                            RpcClient client = new RpcClient(host, port);
                            long time = System.currentTimeMillis();
                            RpcResponse response = client.send(request);
                            log.debug("time: {}ms", System.currentTimeMillis() - time);
                            if (response == null) {
                                throw new RuntimeException("response is null");
                            }
                            // 返回 RPC 响应结果
                            if (response.hasException()) {
                                throw response.getException();
                            } else {
                                return response.getResult();
                            }
                        }
                    }
            );
    		
    		proxyCache.put(interfaceClass, serviceVersion, proxy);
		}
        return (T) proxy;
    }
}
